package org.gbif.action;

import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.util.ServletContextAware;
import org.gbif.service.RESTManager;

import org.springframework.beans.factory.annotation.Autowired;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

/**A generic REST action class with methods for each CRUD operation and an index listing.
 * 
 * The default EXECUTE() method handles all requests for a single resource. 
 * The http header is read in that case and depending on the request type the request is then handled with the specific CRUD method.
 * According to common REST practice this is:
 *  GET=read
 *  PUT or POST=update 
 *  DELETE=delete
 *  
 * The INDEX() method creates a new resource when called as POST or PUT
 * and return an index listing for GET requests. 
 *  GET=listing
 *  PUT or POST=create
 *  
 *  The action makes heavily use of a generic manager which is not wired in this base class.
 *  Make sure your concrete action class is setting the manager property!
 *  
 *  This action makes use of the model driven interceptor. All request parameters are therefore directly applied to model properties.
 *  We use the parameter x to request a specific model ID because of that as it needs to be a parameter name not used in the model entity. 
 *  The obvious parameter name id is usually already taken for the model, so thats why its x (and to keep it short).
 *  
 *  It is the responsibility of the struts.xml mappings to define good http codes for the response.
 *  This action returns the following struts result names that then can be mapped to whatever http code:
 *  "success" for GET based actions, i.e. read/list
 *  "ok" for successful returns of "void" actions, i.e. delete/update
 *  "created" for create
 *  "not_found" if model could not be loaded via id parameter
 *  "bad_request" if strange http request types have been used
 *  "error" in case of unknown errors
 * 
 * As a help here are a list of common http return codes to be used in your struts.xml mappings: 
200 OK: everything went awesome.
201 CREATED: new resource created
400 Bad Request: your request is invalid, and we'll return an error message that tells you why. 
401 Not Authorized: either you need to provide authentication credentials, or the credentials provided aren't valid.
404 Not Found: either you're requesting an invalid URI or the resource in question doesn't exist 
500 Internal Server Error

 * @author markus
 *
 * @param <T>
 * @param <R>
 */
public   class RESTBaseAction<T> extends ActionSupport implements Preparable, ModelDriven<T>, ServletRequestAware{
	protected final Log log = LogFactory.getLog(getClass());
	public static final String NOT_FOUND = "not_found";
	public static final String BAD_REQUEST = "bad_request";
	public static final String CREATED = "created";
	public static final String OK = "ok";
	private String id;
	protected T model;
	protected List<T> models;
	protected RESTManager<T> manager;
	private HttpServletRequest request;
	private boolean isNew=false;
	
	public void prepare() throws Exception {
		if (id!=null){
			model = manager.read(id);
		}else{
			model = manager.newInstance();
			isNew=true;
		}
	}

	public String execute(){
		// all resource operations require a valid resource
		// check if model exists or return 404 otherwise
		if (model==null || isNew){
			return NOT_FOUND;
		}			
		if (request.getMethod().equalsIgnoreCase("PUT") || request.getMethod().equalsIgnoreCase("POST")){
			return update();
		}
		if (request.getMethod().equalsIgnoreCase("DELETE")){
			return delete();
		}
		if (request.getMethod().equalsIgnoreCase("GET")){
			return read();
		}
		// other http request types are not supported
		return BAD_REQUEST;
	}
	
	public String index(){
		if (request.getMethod().equalsIgnoreCase("PUT") || request.getMethod().equalsIgnoreCase("POST")){
			return create();
		}
		if (request.getMethod().equalsIgnoreCase("GET")){
			return list();
		}
		// other http request types are not supported
		return BAD_REQUEST;
	}
	

	public String list(){
		log.debug("REST list");
		models = manager.list();
		return SUCCESS;
	}

	public String create(){
		log.debug("REST create");
		manager.create(model);
		return CREATED;
	}
	public String read(){
		log.debug("REST read");
		// object already set via modelDriven
		if (model==null || isNew){
			return NOT_FOUND;
		}
		return SUCCESS;
	}

	public String update(){
		log.debug("REST update");
		// check again if model exists, method could be wired from struts.xml directly without going through execute()
		if (model==null || isNew){
			return NOT_FOUND;
		}
		manager.update(model);
		return OK;
	}
	public String delete(){
		log.debug("REST delete");
		// check again if model exists, method could be wired from struts.xml directly without going through execute()
		if (model==null || isNew){
			return NOT_FOUND;
		}
		manager.delete(model);
		return OK;
	}

	public T getModel() {		
		return model;
	}
	
	public void setServletRequest(HttpServletRequest reqObj) {
		this.request=reqObj;
		id=reqObj.getParameter("id");
	}

	public List<T> getModels() {
		return models;
	}

}
